﻿using System;
using System.Collections.Generic;
using Draco.OWF.Exceptions;
using System.Xml.Serialization;

namespace Draco.OWF.Metadata.Impl
{
    [XmlRoot("WorkflowProcess")]
    public class WorkflowModelImpl: WorkflowModel
    {
        public WorkflowModelImpl() : this("", "") { }
        public WorkflowModelImpl(String name) : this("", name) { }
        public WorkflowModelImpl(String id, String name)
        {
            if (String.IsNullOrEmpty(name))
                name = "untitled workflow";
            this.Id = id;
            this.Name = name;
            this.Header = new WorkflowHeader();
            this.Header.CreateDate = DateTime.Now;
        }

        [XmlIgnore]
        public override bool Readonly { get; set; }

        public override ActivityModel GetEntry()
        {
            foreach(var ac in m_Activities)
            {
                if (ac.Entry)
                    return ac;
            }
            return null;
        }

        [XmlIgnore]
        public override ActivityModel EndActivity
        {
            get { return m_EndActivity; }
            set { m_EndActivity = value; }
        }

        public override ActivityModel GetActivity(string activityId)
        {
            if (this.m_Activities.Count > 0)
            {
                foreach (ActivityModel ac in m_Activities)
                {
                    if (ac.Id == activityId)
                        return ac;
                }
            }
            return null;
        }

        public override RouteModel GetRoute(string routeId)
        {
            if (this.m_Routes.Count > 0)
            {
                foreach (RouteModel route in this.m_Routes)
                {
                    if (route.Id == routeId)
                    {
                        return route;
                    }
                }
            }
            return null;
        }
        public override RouteModel GetRoute(string fromAcId, string toAcId)
        {
            if (this.m_Routes.Count > 0)
            {
                foreach (RouteModel route in this.m_Routes)
                {
                    if (route.FromId == fromAcId && route.ToId == toAcId)
                    {
                        return route;
                    }
                }
            }
            return null;
        }

        public override List<RouteModel> GetFromActivityRoutes(string activityId)
        {
            List<RouteModel> list = new List<RouteModel>();
            if (this.m_Routes.Count > 0)
            {
                foreach (var route in this.m_Routes)
                {
                    if (route.FromId == activityId)
                    {
                        list.Add(route);
                    }
                }
            }
            return list;
        }

        public override List<RouteModel> GetToActivityRoutes(string activityId)
        {
            List<RouteModel> list = new List<RouteModel>();
            if (this.m_Routes.Count > 0)
            {
                foreach (var route in this.m_Routes)
                {
                    if (route.ToId == activityId)
                    {
                        list.Add(route);
                    }
                }
            }
            return list;
        }

        public override List<ActivityModel> GetNextActivities(string activityId)
        {
            List<ActivityModel> list = new List<ActivityModel>();
            List<RouteModel> routes = GetFromActivityRoutes(activityId);
            foreach (var route in routes)
            {
                ActivityModel ac = this.GetActivity(route.ToId);
                list.Add(ac);
            }
            return list;
        }

        public override List<ActivityModel> GetPreviousActivities(string activityId)
        {
            List<ActivityModel> list = new List<ActivityModel>();
            List<RouteModel> routes = GetToActivityRoutes(activityId);
            foreach (var route in routes)
            {
                ActivityModel ac = this.GetActivity(route.FromId);
                list.Add(ac);
            }
            return list;
        }

        public override void AddActivity(ActivityModel activity)
        {
            if (activity == null)
                throw new ArgumentNullException("Argument [activity] is null");
            if (this.Readonly)
                throw new WorkflowReadonlyException();
            ActivityModel ac = this.GetActivity(activity.Id);
            if (ac != null)
            {
                throw new WorkflowException("Activity Exist,activity Id:" + activity.Id);
            }
            this.m_Activities.Add(activity);
        }

        public override void AddRoute(RouteModel route)
        {
            if (route == null)
                throw new ArgumentNullException("Argument [route] is null");
            if (this.Readonly)
                throw new WorkflowReadonlyException();
            RouteModel r = this.GetRoute(route.Id);
            if (r != null)
            {
                throw new WorkflowException("Route Exist,route Id:" + route.Id);
            }
            this.m_Routes.Add(route);
        }

        public override void RemoveActivity(string activityId)
        {
            ActivityModel ac = this.GetActivity(activityId);
            if (ac != null)
                this.m_Activities.Remove(ac);
        }

        public override void RemoveRoute(string routeId)
        {
            RouteModel route = this.GetRoute(routeId);
            if (route != null)
                this.m_Routes.Remove(route);
        }

        public override void Clear()
        {
            this.m_Activities.Clear();
            this.m_Routes.Clear();
        }

        public override bool Check()
        {
            ActivityModel startAc = this.GetEntry();
            if (this.m_Activities.Count == 0 && this.m_Routes.Count == 0)
                return true;    //empty
            if (startAc == null || this.EndActivity == null)
                return false;   // no start entry
            if (this.m_Activities.Count == 1 && this.m_Routes.Count == 0)
                return true; //only one activity
            foreach (var route in this.m_Routes)
            {
                ActivityModel acFrom = this.GetActivity(route.FromId);
                ActivityModel acTo = this.GetActivity(route.ToId);
                if (acFrom == null || acTo == null)
                    return false;   //Invalid route
            }
            foreach (ActivityModel activity in this.m_Activities)
            {
                List<RouteModel> from = this.GetFromActivityRoutes(activity.Id);
                List<RouteModel> to = this.GetToActivityRoutes(activity.Id);
                if (from.Count == 0 && to.Count == 0) //Unreachable activity
                    return false;
                if (from.Count == 0 && activity.Id != this.EndActivity.Id)
                    return false;
                if (to.Count == 0 && activity.Id != startAc.Id)
                    return false;
            }
            return true;
        }
    }
}
